﻿using System;

namespace Adaptive.Foundation.Tokenizers
{
    /// <summary>
    /// Provides a storage and management mechanism for assigning character type information 
    /// to character values.
    /// </summary>
    /// <remarks>
    /// This class can be customized for any particular character tokenization implementation.
    /// </remarks>
    public class CharacterTypeTable : AiObjectBase
    {
        /*--------------------------------------------------------------------------------
         * Public Events
         *------------------------------------------------------------------------------*/
        #region Public Events
        #endregion

        /*--------------------------------------------------------------------------------
         * Private Member Declarations
         *------------------------------------------------------------------------------*/
        #region Private Member Declarations
        /// <summary>
        /// The list of entries.
        /// </summary>
        private CharacterTypeEntryCollection _characterList;
        #endregion

        /*--------------------------------------------------------------------------------
         * Constructor / Dispose Methods
         *------------------------------------------------------------------------------*/
        #region Constructor / Dispose Methods
        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <remarks>
        /// This is the default constructor.
        /// </remarks>
        public CharacterTypeTable()
            : base()
        {
            _characterList = new CharacterTypeEntryCollection();
            SetDefaults();
        }
        /// <summary>
        /// Deallocates internally allocated objects.
        /// </summary>
        /// <param name="disposing">
        /// A value indicating whether the object is being disposed.
        /// </param>
        protected override void Dispose(bool disposing)
        {
            if ((!Disposed) && (disposing))
            {
                if (_characterList != null)
                    _characterList.Dispose();
            }

            _characterList = null;
            base.Dispose(disposing);
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Properties
         *------------------------------------------------------------------------------*/
        #region Public Properties
        /// <summary>
        /// Gets the reference to the list of defined character values, types, and operations.
        /// </summary>
        /// <value>
        /// A <see cref="CharacterTypeEntryCollection"/> instance.
        /// </value>
        public CharacterTypeEntryCollection List
        {
            get { return _characterList; }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Protected Methods
         *------------------------------------------------------------------------------*/
        #region Protected Methods
        /// <summary>
        /// Sets the type assignment for the provided character entry.
        /// </summary>
        /// <param name="typeEntry">
        /// A <see cref="CharacterTypeEntry"/> instance representing the character
        /// value type.
        /// </param>
        /// <remarks>
        /// This method is invoked from the <see cref="SetDefaults"/> method and is intended
        /// to be overridden for cutom type assignment.
        /// </remarks>
        protected virtual void SetTypeAssignment(CharacterTypeEntry typeEntry)
        {
            switch (typeEntry.Value)
            {
                case CharacterTokenValues.Dash:
                case CharacterTokenValues.SemiColon:
                case CharacterTokenValues.Colon:
                case CharacterTokenValues.Slash:
                case CharacterTokenValues.Backslash:
                case CharacterTokenValues.Period:
                    typeEntry.AssignedType = CharacterTokenTypes.Seperator;
                    break;

                case CharacterTokenValues.NumericDigit:
                    typeEntry.AssignedType = CharacterTokenTypes.NumberDigit;
                    break;

                case CharacterTokenValues.OpenAngle:
                case CharacterTokenValues.OpenBrace:
                case CharacterTokenValues.OpenBracket:
                case CharacterTokenValues.OpenParenthesis:
                    typeEntry.AssignedType = CharacterTokenTypes.ListDelimiterStart;
                    break;

                case CharacterTokenValues.CloseAngle:
                case CharacterTokenValues.CloseBrace:
                case CharacterTokenValues.CloseBracket:
                case CharacterTokenValues.CloseParenthesis:
                    typeEntry.AssignedType = CharacterTokenTypes.ListDelimiterEnd;
                    break;

                case CharacterTokenValues.SingleQuote:
                case CharacterTokenValues.DoubleQuote:
                    typeEntry.AssignedType = CharacterTokenTypes.StringDelimiter;
                    break;

                case CharacterTokenValues.QuestionMark:
                case CharacterTokenValues.ExclamationMark:
                case CharacterTokenValues.AtSign:
                case CharacterTokenValues.DollarSign:
                case CharacterTokenValues.NumberSign:
                case CharacterTokenValues.Tilde:
                case CharacterTokenValues.PercentSign:
                case CharacterTokenValues.Caret:
                case CharacterTokenValues.Ampersand:
                case CharacterTokenValues.Asterisk:
                case CharacterTokenValues.AdditionSign:
                case CharacterTokenValues.EqualsSign:
                    typeEntry.AssignedType = CharacterTokenTypes.Punctuation;
                    break;

                case CharacterTokenValues.CarriageReturn:
                case CharacterTokenValues.Linefeed:
                case CharacterTokenValues.Space:
                case CharacterTokenValues.Tab:
                case CharacterTokenValues.Backspace:
                case CharacterTokenValues.Bell:
                    typeEntry.AssignedType = CharacterTokenTypes.Whitespace;
                    break;

                case CharacterTokenValues.HighOrderCharacter:
                case CharacterTokenValues.HighOrderMaxAscii:
                case CharacterTokenValues.NullCharacter:
                    typeEntry.AssignedType = CharacterTokenTypes.SpecialCharacter;
                    break;

                case CharacterTokenValues.Undefined:
                    typeEntry.AssignedType = CharacterTokenTypes.Unspecified;
                    break;
            }
        }
        /// <summary>
        /// Sets the operation assignment for the provided character entry.
        /// </summary>
        /// <param name="typeEntry">
        /// A <see cref="CharacterTypeEntry"/> instance representing the character
        /// value type.
        /// </param>
        /// <remarks>
        /// This method is invoked from the <see cref="SetDefaults"/> method and is intended
        /// to be overridden for cutom operation assignment.
        /// </remarks>
        protected virtual void SetOperation(CharacterTypeEntry typeEntry)
        {
            switch (typeEntry.Value)
            {
                case CharacterTokenValues.Dash:
                case CharacterTokenValues.SemiColon:
                case CharacterTokenValues.Colon:
                case CharacterTokenValues.Slash:
                case CharacterTokenValues.Backslash:
                case CharacterTokenValues.Period:
                    typeEntry.AssignedOperation = CharacterTokenOperations.None;
                    break;

                case CharacterTokenValues.NumericDigit:
                    typeEntry.AssignedOperation = CharacterTokenOperations.ProcessNumberStart;
                    break;

                case CharacterTokenValues.OpenAngle:
                case CharacterTokenValues.OpenBrace:
                case CharacterTokenValues.OpenBracket:
                case CharacterTokenValues.OpenParenthesis:
                    typeEntry.AssignedOperation = CharacterTokenOperations.ProcessSubListStart;
                    break;

                case CharacterTokenValues.CloseAngle:
                case CharacterTokenValues.CloseBrace:
                case CharacterTokenValues.CloseBracket:
                case CharacterTokenValues.CloseParenthesis:
                    typeEntry.AssignedOperation = CharacterTokenOperations.ProcessSubListEnd;
                    break;

                case CharacterTokenValues.SingleQuote:
                case CharacterTokenValues.DoubleQuote:
                    typeEntry.AssignedOperation = CharacterTokenOperations.ProcessStringStart;
                    break;

                case CharacterTokenValues.QuestionMark:
                case CharacterTokenValues.ExclamationMark:
                case CharacterTokenValues.AtSign:
                case CharacterTokenValues.DollarSign:
                case CharacterTokenValues.NumberSign:
                case CharacterTokenValues.Tilde:
                case CharacterTokenValues.PercentSign:
                case CharacterTokenValues.Caret:
                case CharacterTokenValues.Ampersand:
                case CharacterTokenValues.Asterisk:
                case CharacterTokenValues.AdditionSign:
                case CharacterTokenValues.EqualsSign:
                    typeEntry.AssignedOperation = CharacterTokenOperations.None;
                    break;

                case CharacterTokenValues.CarriageReturn:
                case CharacterTokenValues.Linefeed:
                case CharacterTokenValues.Space:
                case CharacterTokenValues.Tab:
                case CharacterTokenValues.Backspace:
                case CharacterTokenValues.Bell:
                    typeEntry.AssignedOperation = CharacterTokenOperations.None;
                    break;

                case CharacterTokenValues.HighOrderCharacter:
                case CharacterTokenValues.HighOrderMaxAscii:
                case CharacterTokenValues.NullCharacter:
                    typeEntry.AssignedOperation = CharacterTokenOperations.None;
                    break;

                case CharacterTokenValues.Undefined:
                    typeEntry.AssignedOperation = CharacterTokenOperations.None;
                    break;
            }
        }
        /// <summary>
        /// Sets the default contents of the internal list on construction.
        /// </summary>
        protected virtual void SetDefaults()
        {
            CharacterTypeEntry newEntry = null;             //New instance.
            int count = 0;                                  //Iteration counter.
            int length = 0;                                 //Iteration limit.

            length = (int)CharacterTokenValues.Undefined;
            for (count = 1; count < length; count++)
            {
                newEntry = new CharacterTypeEntry((CharacterTokenValues)count);
                SetTypeAssignment(newEntry);
                SetOperation(newEntry);
                _characterList.Add(newEntry);
            }
        }
        #endregion

        /*--------------------------------------------------------------------------------
         * Public Methods / Functions
         *------------------------------------------------------------------------------*/
        #region Public Methods / Functions
        /// <summary>
        /// Assigns the specified token type to the specified token value.
        /// </summary>
        /// <param name="characterValue">
        /// A <see cref="CharacterTokenValues"/> enumerated value indicating the type of 
        /// character.
        /// </param>
        /// <param name="type">
        /// A <see cref="CharacterTokenTypes"/> enumerated value.
        /// </param>
        public virtual void Assign(CharacterTokenValues characterValue, CharacterTokenTypes type)
        {
            CharacterTypeEntry newEntry = null;

            if (_characterList[characterValue.ToString()] == null)
            {
                newEntry = new CharacterTypeEntry(characterValue);
                newEntry.AssignedType = type;
                _characterList.Add(newEntry);
            }
            else
            {
                newEntry = _characterList[characterValue.ToString()];
                newEntry.AssignedType = type;
            }
        }
        /// <summary>
        /// Assigns the specified token operation to the specified token value.
        /// </summary>
        /// <param name="characterValue">
        /// A <see cref="CharacterTokenValues"/> enumerated value indicating the type of 
        /// character.
        /// </param>
        /// <param name="operation">
        /// A <see cref="CharacterTokenOperations"/> enumerated value.
        /// </param>
        public virtual void Assign(CharacterTokenValues characterValue, CharacterTokenOperations operation)
        {
            CharacterTypeEntry newEntry = null;

            if (_characterList[characterValue.ToString()] == null)
            {
                newEntry = new CharacterTypeEntry(characterValue);
                newEntry.AssignedOperation = operation;
                _characterList.Add(newEntry);
            }
            else
            {
                newEntry = _characterList[characterValue.ToString()];
                newEntry.AssignedOperation = operation;
            }
        }
        /// <summary>
        /// Assigns the specified token operation to the specified token value.
        /// </summary>
        /// <param name="characterValue">
        /// A <see cref="CharacterTokenValues"/> enumerated value indicating the type of 
        /// character.
        /// </param>
        /// <param name="type">
        /// A <see cref="CharacterTokenTypes"/> enumerated value.
        /// </param>
        /// <param name="operation">
        /// A <see cref="CharacterTokenOperations"/> enumerated value.
        /// </param>
        public virtual void Assign(CharacterTokenValues characterValue, CharacterTokenTypes type, CharacterTokenOperations operation)
        {
            CharacterTypeEntry newEntry = null;

            if (_characterList[characterValue.ToString()] == null)
            {
                newEntry = new CharacterTypeEntry(characterValue);
                newEntry.AssignedOperation = operation;
                newEntry.AssignedType = type;
                _characterList.Add(newEntry);
            }
            else
            {
                newEntry = _characterList[characterValue.ToString()];
                newEntry.AssignedOperation = operation;
                newEntry.AssignedType = type;
            }
        }
        /// <summary>
        /// Assigns the specified token type to the specified token value.
        /// </summary>
        /// <param name="characterValue">
        /// The character.
        /// </param>
        /// <param name="type">
        /// A <see cref="CharacterTokenTypes"/> enumerated value.
        /// </param>
        public virtual void Assign(char characterValue, CharacterTokenTypes type)
        {
            CharacterTypeEntry newEntry = null;
            CharacterTokenValues tokenValue = CharacterTokenValues.None;

            tokenValue = CharacterTokenFactory.GetTokenValue(characterValue);
            if (_characterList[tokenValue.ToString()] == null)
            {
                newEntry = new CharacterTypeEntry(tokenValue);
                newEntry.AssignedType = type;
                _characterList.Add(newEntry);
            }
            else
            {
                newEntry = _characterList[tokenValue.ToString()];
                newEntry.AssignedType = type;
            }
        }
        /// <summary>
        /// Assigns the specified token operation to the specified token value.
        /// </summary>
        /// <param name="characterValue">
        /// The character.
        /// </param>
        /// <param name="operation">
        /// A <see cref="CharacterTokenOperations"/> enumerated value.
        /// </param>
        public virtual void Assign(char characterValue, CharacterTokenOperations operation)
        {
            CharacterTypeEntry newEntry = null;
            CharacterTokenValues tokenValue = CharacterTokenValues.None;

            tokenValue = CharacterTokenFactory.GetTokenValue(characterValue);

            if (_characterList[tokenValue.ToString()] == null)
            {
                newEntry = new CharacterTypeEntry(tokenValue);
                newEntry.AssignedOperation = operation;
                _characterList.Add(newEntry);
            }
            else
            {
                newEntry = _characterList[tokenValue.ToString()];
                newEntry.AssignedOperation = operation;
            }
        }
        /// <summary>
        /// Assigns the specified token operation to the specified token value.
        /// </summary>
        /// <param name="characterValue">
        /// The character.
        /// </param>
        /// <param name="type">
        /// A <see cref="CharacterTokenOperations"/> enumerated value.
        /// </param>
        /// <param name="operation">
        /// A <see cref="CharacterTokenOperations"/> enumerated value.
        /// </param>
        public virtual void Assign(char characterValue, CharacterTokenTypes type, CharacterTokenOperations operation)
        {
            CharacterTypeEntry newEntry = null;
            CharacterTokenValues tokenValue = CharacterTokenValues.None;

            tokenValue = CharacterTokenFactory.GetTokenValue(characterValue);

            if (_characterList[tokenValue.ToString()] == null)
            {
                newEntry = new CharacterTypeEntry(tokenValue);
                newEntry.AssignedOperation = operation;
                newEntry.AssignedType = type;
                _characterList.Add(newEntry);
            }
            else
            {
                newEntry = _characterList[tokenValue.ToString()];
                newEntry.AssignedOperation = operation;
                newEntry.AssignedType = type;
            }
        }
        /// <summary>
        /// Gets the assigned token type for the specified character.
        /// </summary>
        /// <param name="characterValue">
        /// The character to be queried.
        /// </param>
        /// <returns>
        /// A <see cref="CharacterTokenTypes"/> enumerated value.
        /// </returns>
        public CharacterTokenTypes GetTypeForCharacter(char characterValue)
        {
            CharacterTokenValues tokenValue = CharacterTokenFactory.GetTokenValue(characterValue);
            return GetTypeForCharacter(tokenValue);
        }
        /// <summary>
        /// Gets the assigned token type for the specified character.
        /// </summary>
        /// <param name="characterValue">
        /// A <see cref="CharacterTokenValues"/> enumerated value indicating the character to be queried.
        /// </param>
        /// <returns>
        /// A <see cref="CharacterTokenTypes"/> enumerated value.
        /// </returns>
        public virtual CharacterTokenTypes GetTypeForCharacter(CharacterTokenValues characterValue)
        {
            CharacterTokenTypes returnValue = CharacterTokenTypes.None;
            CharacterTypeEntry entry = null;

            entry = _characterList[characterValue.ToString()];
            if (entry != null)
                returnValue = entry.AssignedType;

            return returnValue;
        }
        /// <summary>
        /// Gets the assigned token type for the specified character.
        /// </summary>
        /// <param name="characterValue">
        /// The character to be queried.
        /// </param>
        /// <returns>
        /// A <see cref="CharacterTokenOperations"/> enumerated value.
        /// </returns>
        public CharacterTokenOperations GetOperationForCharacter(char characterValue)
        {
            CharacterTokenValues tokenValue = CharacterTokenFactory.GetTokenValue(characterValue);
            return GetOperationForCharacter(characterValue);
        }
        /// <summary>
        /// Gets the assigned token type for the specified character.
        /// </summary>
        /// <param name="characterValue">
        /// A <see cref="CharacterTokenValues"/> enumerated value indicating the character to be queried.
        /// </param>
        /// <returns>
        /// A <see cref="CharacterTokenOperations"/> enumerated value.
        /// </returns>
        public virtual CharacterTokenOperations GetOperationForCharacter(CharacterTokenValues characterValue)
        {
            CharacterTokenOperations returnValue = CharacterTokenOperations.None;
            CharacterTypeEntry entry = null;

            entry = _characterList[characterValue.ToString()];
            if (entry != null)
                returnValue = entry.AssignedOperation;

            return returnValue;
        }
        /// <summary>
        /// Gets the list of token entries for the specified token type.
        /// </summary>
        /// <param name="type">
        /// A <see cref="CharacterTokenTypes"/> indicating the token type.
        /// </param>
        /// <returns>
        /// A <see cref="CharacterTypeEntryCollection"/> containing the entries.
        /// </returns>
        public CharacterTypeEntryCollection GetTokensForType(CharacterTokenTypes type)
        {
            CharacterTypeEntryCollection returnValue = null;

            returnValue = new CharacterTypeEntryCollection();
            foreach (CharacterTypeEntry entry in _characterList)
            {
                if (entry.AssignedType == type)
                    returnValue.Add(entry);
            }
            return returnValue;
        }
        /// <summary>
        /// Gets the list of token entries for the specified token operation.
        /// </summary>
        /// <param name="operation">
        /// A <see cref="CharacterTokenOperations"/> indicating the token operation.
        /// </param>
        /// <returns>
        /// A <see cref="CharacterTypeEntryCollection"/> containing the entries.
        /// </returns>
        public CharacterTypeEntryCollection GetTokensForOperation(CharacterTokenOperations operation)
        {
            CharacterTypeEntryCollection returnValue = null;

            returnValue = new CharacterTypeEntryCollection();
            foreach (CharacterTypeEntry entry in _characterList)
            {
                if (entry.AssignedOperation == operation)
                    returnValue.Add(entry);
            }
            return returnValue;
        }
        /// <summary>
        /// Determines whether the specified character is a registered token.
        /// </summary>
        /// <param name="character">A string containing the character to be checked.</param>
        /// <returns>
        /// <b>true</b> if the character represents a registered token;
        /// otherwise, <b>false</b>.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        /// Throw nwhen the <i>character</i> parameter is <b>null</b> or empty.
        /// </exception>
        public virtual bool IsToken(string character)
        {
            if (string.IsNullOrEmpty(character))
                throw new ArgumentNullException("character");

            return IsToken(character[0]);
        }
        /// <summary>
        /// Determines whether the specified character is a registered token.
        /// </summary>
        /// <param name="character">The character to be checked.</param>
        /// <returns>
        /// <b>true</b> if the character represents a registered token;
        /// otherwise, <b>false</b>.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Throw nwhen the <i>character</i> parameter is less than zero or greater than 255.
        /// </exception>
        public virtual bool IsToken(int character)
        {
            if ((character < 0) || (character > 255))
                throw new ArgumentOutOfRangeException("character");

            return IsToken((char)character);
        }
        /// <summary>
        /// Determines whether the specified character is a registered token.
        /// </summary>
        /// <param name="character">The character to be checked.</param>
        /// <returns>
        /// <b>true</b> if the character represents a registered token;
        /// otherwise, <b>false</b>.
        /// </returns>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Throw nwhen the <i>character</i> parameter is less than zero or greater than 255.
        /// </exception>
        public virtual bool IsToken(short character)
        {
            if ((character < 0) || (character > 255))
                throw new ArgumentOutOfRangeException("character");

            return IsToken((char)character);
        }
        /// <summary>
        /// Determines whether the specified character is a registered token.
        /// </summary>
        /// <param name="character">The character to be checked.</param>
        /// <returns>
        /// <b>true</b> if the character represents a registered token;
        /// otherwise, <b>false</b>.
        /// </returns>
        public virtual bool IsToken(byte character)
        {
            return IsToken((char)character);
        }
        /// <summary>
        /// Determines whether the specified character is a registered token.
        /// </summary>
        /// <param name="character">The character to be checked.</param>
        /// <returns>
        /// <b>true</b> if the character represents a registered token;
        /// otherwise, <b>false</b>.
        /// </returns>
        public virtual bool IsToken(char character)
        {
            bool returnValue = false;

            returnValue = (_characterList[CharacterTokenFactory.GetTokenValue(character).ToString()] != null);

            return returnValue;
        }
        #endregion
    }
}